import asyncio
import time

import yaml
import serial_asyncio
from typing import Dict, Any, Optional, Tuple
import serial


class AsyncSerialDevice:
    """单个设备的异步串口通信类（独立串口，不共享）"""

    def __init__(self, config):
        config = config['parameters']

        self.config = config
        # 串口参数
        self.port = config['port']
        self.baudrate = config.get('baud_rate')
        self.bytesize = self._get_bytesize(config.get('bytesize'))
        self.parity = config.get('parity').upper()
        self.stopbits = self._get_stopbits(config.get('stopbits'))
        self.timeout = config.get('timeout')

        # 异步流和锁
        self.reader: Optional[asyncio.StreamReader] = None
        self.writer: Optional[asyncio.StreamWriter] = None
        self.lock = asyncio.Lock()  # 确保单设备操作的原子性

    def __str__(self):
        return f"port = {self.port}, baudrate = {self.baudrate}, bytesize = {self.bytesize}, parity = {self.parity}, stopbits = {self.stopbits}"

    @staticmethod
    def _get_bytesize(bytesize: int) -> int:
        mapping = {8: serial.EIGHTBITS, 7: serial.SEVENBITS, 6: serial.SIXBITS, 5: serial.FIVEBITS}
        return mapping.get(bytesize, serial.EIGHTBITS)

    @staticmethod
    def _get_stopbits(stopbits: float) -> float:
        mapping = {1: serial.STOPBITS_ONE, 1.5: serial.STOPBITS_ONE_POINT_FIVE, 2: serial.STOPBITS_TWO}
        return mapping.get(stopbits, serial.STOPBITS_ONE)

    async def connect(self) -> bool:
        """异步连接到设备的独立串口（增加详细日志）"""
        current_time = time.strftime("%Y-%m-%d %H:%M:%S")
        print(f"{current_time} 开始连接串口：{self.port}（波特率：{self.baudrate}）")
        try:
            self.reader, self.writer = await serial_asyncio.open_serial_connection(
                url=self.port,
                baudrate=self.baudrate,
                bytesize=self.bytesize,
                parity=self.parity,
                stopbits=self.stopbits,
                timeout=self.timeout
            )
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"{current_time} 串口连接成功！reader和writer已初始化")
            return True
        except Exception as e:
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            # 打印具体错误原因（端口不存在？权限不足？）
            print(f"{current_time} 串口连接失败: {str(e)}（端口：{self.port}）")
            return False

    async def disconnect(self) -> None:
        """断开串口连接"""
        if self.writer:
            self.writer.close()
            await self.writer.wait_closed()

    async def send_hex(self, hex_str: str, sleep_time=0.05) -> Tuple[bool, Optional[str]]:
        """发送十六进制字符串指令并返回响应（无响应时返回False）"""
        if not self.reader or not self.writer:
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"{current_time} 错误：reader或writer未初始化")
            return False, None

        try:
            data = bytes.fromhex(hex_str)
            if len(data) < 3:
                raise ValueError("指令长度不足（至少3字节）")
            func_code = data[1]  # Modbus功能码
            send_len = len(data)
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"{current_time} 发送指令: {hex_str}")
            print(f"{current_time} 功能码: {func_code}, 发送长度: {send_len}")

        except ValueError as e:
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"{current_time} 格式错误: {str(e)}")
            return False, None

        try:
            async with self.lock:
                # 发送数据
                self.writer.write(data)
                await self.writer.drain()

                # 读取响应 - 增加每次读取的字节数
                response = b''
                start_time = asyncio.get_event_loop().time()
                total_timeout = self.timeout
                read_chunk_size = 6  # 每次读取6字节，提高效率
                # current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                # print(f"{current_time} 开始接收响应，超时设置: {total_timeout}秒")

                while (asyncio.get_event_loop().time() - start_time) < total_timeout:
                    try:
                        # 尝试读取更多字节
                        chunk = await asyncio.wait_for(
                            self.reader.read(read_chunk_size),
                            timeout=min(0.1, total_timeout - (asyncio.get_event_loop().time() - start_time))
                        )
                        if not chunk:
                            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                            # print(f"{current_time} 读取到空数据，退出循环")
                            break  # 没有更多数据
                        response += chunk
                        current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                        # print(f"{current_time} 接收到新数据块 ({len(chunk)}字节), 累计响应长度: {len(response)}")
                        # print(f"{current_time} 当前响应内容: {response.hex()}")

                        # 检查响应是否完整
                        if self._is_response_complete(response, func_code, send_len):
                            # current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                            # print(f"{current_time} 响应已完整，退出循环")
                            break

                    except asyncio.TimeoutError:
                        # 超时但已有部分响应，继续处理
                        current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                        if response:
                            # print(f"{current_time} 读取超时，但已有部分响应数据")
                            break
                        else:
                            # print(f"{current_time} 读取超时且无响应数据")
                            return False, None

                await asyncio.sleep(sleep_time)

                # 处理响应结果
                if not response:
                    current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                    # print(f"{current_time} 没有接收到任何响应")
                    return False, None
                else:
                    current_time = time.strftime("%Y-%m-%d %H:%M:%S")
                    # print(f"{current_time} 完整响应: {response.hex()}")
                    return True, response.hex()

        except Exception as e:
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"{current_time} 发生未知错误: {str(e)}")
            return False, None

    def _is_response_complete(self, response: bytes, func_code: int, send_len: int) -> bool:
        """判断响应是否完整"""
        resp_len = len(response)

        # 至少需要3个字节才能开始解析
        if resp_len < 3:
            return False

        # 检查异常响应 (功能码 + 0x80)
        if response[0] == response[0] and response[1] == (func_code + 0x80) and resp_len >= 5:
            return True

        # 根据功能码判断响应是否完整
        if func_code in [5, 6]:  # 写入类 (响应与发送长度相同)
            return resp_len >= send_len

        elif func_code in [1, 2, 3, 4]:  # 读取类 (按数据长度判断)
            if resp_len >= 3:
                data_len = response[2]  # 数据长度字节
                total_resp_len = 3 + data_len + 2  # 地址(1) + 功能码(1) + 数据长度(1) + 数据(n) + CRC(2)
                return resp_len >= total_resp_len

        else:  # 其他功能码 (默认至少6字节)
            return resp_len >= 6

    async def send_and_verify(self, write_hex: str, read_hex: str) -> Tuple[bool, str]:
        """发送写指令后立即读取状态进行验证"""
        write_success, write_resp = await self.send_hex(write_hex)
        if not write_success:
            return False, "写指令发送失败"

        read_success, read_resp = await self.send_hex(read_hex)
        if not read_success:
            return False, "读指令发送失败"

        return True, f"写响应: {write_resp} | 读验证: {read_resp}"


async def main():
    try:
        with open("../configs/parameters.yaml", 'r', encoding='utf-8') as f:
            config = yaml.safe_load(f)['parameters']
    except Exception as e:
        print(f"加载配置失败: {str(e)}")
        return

    # 创建设备实例
    devices = {}
    for device_name, device_info in config['device'].items():
        device = AsyncSerialDevice(
            device_name=device_name,
            port=device_info['port'],
            config=config
        )
        devices[device_name] = device

    # 连接所有设备
    connect_tasks = [device.connect() for device in devices.values()]
    await asyncio.gather(*connect_tasks)

    # 测试STS设备
    sts = devices.get('STS')
    if sts and sts.reader is not None and sts.writer is not None:
        sts_write_on = "02050000ff008c09"
        sts_write_off = "020500000000cdf9"
        sts_read = "020100000001fdf9"
        sts_only = "020100010001ac39"

        print("\n=== 测试STS开启 ===")
        success, result = await sts.send_and_verify(sts_write_on, sts_read)
        print(f"开启结果: {'成功' if success else '失败'}，详情: {result}")

        print("\n=== 测试STS关闭 ===")
        success, result = await sts.send_and_verify(sts_write_off, sts_read)
        print(f"关闭结果: {'成功' if success else '失败'}，详情: {result}")

        success, result = await sts.send_hex(sts_write_on)
        print(f"打开结果: {'成功' if success else '失败'}，详情: {result}")

        success, result = await sts.send_hex(sts_only)
        print(f"测试结果: {'成功' if success else '失败'}，详情: {result}")

    # 断开所有连接
    for device in devices.values():
        await device.disconnect()


if __name__ == "__main__":
    asyncio.run(main())